#include <linux/init.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/platform_device.h>
#include <linux/of.h>

struct private_data {
    u64 rx_packet;
    u64 tx_packet;
    u64 rx_bytes;
    u64 tx_bytes;
};

static int loopback_open(struct net_device *dev)
{
    dev_info(&dev->dev, "net device %s open", dev->name);
    return 0;
}

static int loopback_stop(struct net_device *dev)
{
    dev_info(&dev->dev, "net device %s stop", dev->name);
    return 0;  
}

static void loopback_getstats(struct net_device *dev, struct rtnl_link_stats64 *storage)
{
    struct private_data *p = netdev_priv(dev);
    storage->rx_bytes = p->rx_bytes;
    storage->rx_packets = p->rx_packet;
    storage->tx_bytes = p->tx_bytes;
    storage->tx_packets = p->tx_packet;
}

static netdev_tx_t loopback_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct private_data *p = netdev_priv(dev);
    // 停止发送数据包
    netif_stop_queue(dev);
    // 获得包的协议
    skb->protocol = eth_type_trans(skb, dev);
    p->tx_packet += 1;
    p->tx_bytes += skb->len;
    // 将收到的报文再发回协议栈，这个函数一定返回成功
    netif_rx(skb);
    p->rx_packet += 1;
    p->rx_bytes += skb->len;
    dev_info(&dev->dev, "NET_RX_SUCCESS");
    // 开始发送数据包
    netif_start_queue(dev);
    return 0;
}

static struct net_device_ops loopback_netdevice_ops = {
    .ndo_open = loopback_open,
    .ndo_stop = loopback_stop,
    .ndo_start_xmit = loopback_start_xmit,
    .ndo_get_stats64 = loopback_getstats,
};

static void setup(struct net_device *ndev)
{
    ndev->mtu = 64 * 1024; // 74KB
    /* Loopback device		*/
    ndev->type = ARPHRD_LOOPBACK;
    /* @IFF_LOOPBACK: is a loopback net. Volatile. */
    ndev->flags = IFF_LOOPBACK;
    /* @IFF_XMIT_DST_RELEASE: dev_hard_start_xmit() is allowed to release skb->dst
        @IFF_XMIT_DST_RELEASE_PERM: IFF_XMIT_DST_RELEASE not taking into account underlying stacked devices 
        */
    ndev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM);
    ndev->features = NETIF_F_LOOPBACK;
    //ndev->header_ops = eth_header_ops;
    ndev->netdev_ops = &loopback_netdevice_ops;
}

static int rk3588_net_probe(struct platform_device *dev)
{
    int ret = 0;
    struct net_device *net_dev;
    net_dev = alloc_netdev(sizeof(struct private_data), "loopback%d", 1, setup);
    if (!net_dev) {
        dev_err(&dev->dev, "allocate net device failed");
        ret = -ENOMEM;
        goto ALLOCATE_NETDEV_ERR;
    }
    memset(netdev_priv(net_dev), 0, sizeof(struct private_data));

    ret = register_netdev(net_dev);
    if (ret) {
        dev_err(&dev->dev, "register net device failed.ret=%d", ret);
        goto REGISTER_NETDEV_ERR;
    }
    platform_set_drvdata(dev, net_dev);

    return 0;
REGISTER_NETDEV_ERR:
    free_netdev(net_dev);
ALLOCATE_NETDEV_ERR:
    return ret;
}

static int rk3588_net_remove(struct platform_device *dev)
{
    struct net_device *net_dev = platform_get_drvdata(dev);
    unregister_netdev(net_dev);
    free_netdev(net_dev);
    return 0;
}

static struct of_device_id match_table[] = {
    {.compatible = "rk3588-net,guoerba"},
    {}
};

static struct platform_driver g_rk3588_net_driver = {
    .probe = rk3588_net_probe,
    .remove = rk3588_net_remove,
    .driver = {
        .name = "rk3588-net",
        .of_match_table = match_table,
    },
};

static int __init rk3588_net_init(void)
{
    if (platform_driver_register(&g_rk3588_net_driver)) {
        printk("[%s][%d]register platform driver failed\n", __func__, __LINE__);
        return -EBUSY;
    }
    return 0;
}

static void __exit rk3588_net_exit(void)
{
    platform_driver_unregister(&g_rk3588_net_driver);
}

module_init(rk3588_net_init);
module_exit(rk3588_net_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yujie Guo");
MODULE_DESCRIPTION("rk3588 custom loopback device");